﻿/*******************************************************************************
 * AAEngine
 * Copyright (c) 2010 Mike Jarosch
 *
 * Original source PushButton Engine:
 * Copyright (C) 2009 PushButton Labs, LLC
 * For more information see http://www.pushbuttonengine.com
 *
 * This file is licensed under the terms of the MIT license, which is included
 * in the "MIT License.txt" file at the root directory of this SDK.
 ******************************************************************************/

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using AAEngine.Engine;
using AAEngine.Engine.Core;
using AAEngine.Engine.Debug;
using AAEngine.Engine.Entities;

namespace AAEngine.Rendering2D
{
    public class BasicSpatialManager2D : EntityComponent, ISpatialManager2D
    {
        public static readonly string ENTITIES_DIRTY_EVENT = "AAEngine.Rendering2D.BasicSpatialManager2D.ENTITIES_DIRTY_EVENT";

        protected List<ISpatialObject2D> _spatialObjects = new List<ISpatialObject2D>();

        public void AddSpatialObject(ISpatialObject2D spatialObject)
        {
            _spatialObjects.Add(spatialObject);

            Owner.EventDispatcher.DispatchEvent(new Event(ENTITIES_DIRTY_EVENT));
        }

        public void RemoveSpatialObject(ISpatialObject2D spatialObject)
        {
            if (!_spatialObjects.Contains(spatialObject))
            {
                Logger.Warn(this, "RemoveSpatialObject", "The object was not found in this spatial manager.");
                return;
            }
            _spatialObjects.Remove(spatialObject);

            if (Owner != null && Owner.EventDispatcher != null)
            {
                Owner.EventDispatcher.DispatchEvent(new Event(ENTITIES_DIRTY_EVENT));
            }
        }

        public bool QueryRectangle(Box box, ObjectType mask, ref IList<ISpatialObject2D> results)
        {
            Profiler.Enter("QueryRectangle");

            bool foundAny = false;

            foreach (var o in _spatialObjects)
            {
                if (!box.Intersects(o.WorldExtents))
                    continue;

                results.Add(o);
                foundAny = true;
            }

            Profiler.Exit("QueryRectangle");
            return foundAny;
        }

        public bool QueryCircle(Vector2 center, int radius, ObjectType mask, ref IList<ISpatialObject2D> results)
        {
            Profiler.Enter("QueryCircle");

            bool foundAny = false;

            Box scratchRect = new Box();

            foreach (var o in _spatialObjects)
            {
                scratchRect.X = o.WorldExtents.X;
                scratchRect.Y = o.WorldExtents.Y;
                scratchRect.Width = o.WorldExtents.Width;
                scratchRect.Height = o.WorldExtents.Height;

                scratchRect.Inflate(radius, radius);
                if (scratchRect.Contains(center) == ContainmentType.Disjoint)
                    continue;

                results.Add(o);
                foundAny = true;
            }

            Profiler.Exit("QueryCircle");
            return foundAny;
        }

        public bool QueryPoint(Vector2 point, ObjectType mask, ref IList<ISpatialObject2D> results)
        {
            Profiler.Enter("QueryPoint");

            bool foundAny = false;

            foreach (var o in _spatialObjects)
            {
                if (o.WorldExtents.Contains(point) == ContainmentType.Disjoint)
                    continue;

                results.Add(o);
                foundAny = true;
            }

            Profiler.Exit("QueryPoint");
            return foundAny;
        }

        public bool CastRay(Vector2 start, Vector2 end, ObjectType mask, ref RayHitInfo result)
        {
            IList<ISpatialObject2D> results = new List<ISpatialObject2D>();
            Box box = new Box(
                new Vector2(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)),
                new Vector2(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)));

            if (!QueryRectangle(box, mask, ref results))
            {
                return false;
            }

            RayHitInfo bestInfo = null;
            RayHitInfo tempInfo = new RayHitInfo();

            foreach (ISpatialObject2D obj in results)
            {
                if (obj.CastRay(start, end, mask, ref tempInfo))
                {
                    if (bestInfo != null)
                    {
                        bestInfo = new RayHitInfo();
                        bestInfo.CopyFrom(tempInfo);
                    }
                    else if (tempInfo.Time < bestInfo.Time)
                    {
                        bestInfo.CopyFrom(tempInfo);
                    }
                }
            }

            if (bestInfo != null)
            {
                if(result != null)
                {
                    result.CopyFrom(bestInfo);
                }

                return true;
            }

            return false;
        }
    }
}
